#ifndef COMMON_H
#define COMMON_H

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>

#include "acl/acl.h"

#define SUCCESS 0
#define FAILED 1

#define INFO_LOG(fmt,  args...) fprintf(stdout, "[INFO]   " fmt "\n", ##args)
#define WARN_LOG(fmt,  args...) fprintf(stdout, "[WARN]   " fmt "\n", ##args)
#define ERROR_LOG(fmt, args...) fprintf(stderr, "[ERROR]  " fmt "\n", ##args)

char *ReadFile(const std::string &filePath, size_t &fileSize, void *buffer, size_t bufferSize);
bool WriteFile(const std::string &filePath, const void *buffer, size_t size);

struct OperatorDesc {
    explicit OperatorDesc(std::string opType);
    virtual ~OperatorDesc();

    /**
     * Add an input tensor description
     * @param [in] dataType: data type
     * @param [in] numDims: number of dims
     * @param [in] dims: dims
     * @param [in] format: format
     * @return OperatorDesc
     */
    OperatorDesc &AddInputTensorDesc(aclDataType dataType, int numDims, const int64_t *dims, aclFormat format);

    /**
     * Add an output tensor description
     * @param [in] dataType: data type
     * @param [in] numDims: number of dims
     * @param [in] dims: dims
     * @param [in] format: format
     * @return OperatorDesc
     */
    OperatorDesc &AddOutputTensorDesc(aclDataType dataType, int numDims, const int64_t *dims, aclFormat format);

    std::string opType;
    std::vector<aclTensorDesc *> inputDesc;
    std::vector<aclTensorDesc *> outputDesc;
    aclopAttr *opAttr;
};

class  OpRunner {
public:
    explicit OpRunner(OperatorDesc *opDesc);
    virtual ~OpRunner();


    bool Init();
    const size_t NumInputs();
    const size_t NumOutputs();
    const size_t GetInputSize(size_t index) const;
    size_t GetOutputSize(size_t index) const;
    size_t GetInputElementCount(size_t index) const;
    size_t GetOutputElementCount(size_t index) const;


    std::vector<int64_t> GetInputShape(size_t index) const;
    std::vector<int64_t> GetOutputShape(size_t index) const;

    template<typename T>
    T *GetInputBuffer(size_t index) {
        if (index >= numInputs_) {
            ERROR_LOG("index out of range. index = %zu, numInputs = %zu", index, numInputs_);
            return nullptr;
        }
        return reinterpret_cast<T *>(hostInputs_[index]);
    }


    template<typename T>
    const T *GetOutputBuffer(size_t index) {
        if (index >= numOutputs_) {
            ERROR_LOG("index out of range. index = %zu, numOutputs = %zu", index, numOutputs_);
            return nullptr;
        }

        return reinterpret_cast<T *>(hostOutputs_[index]);
    }

    void PrintInput(size_t index, size_t elementsPerRow = 16);
    void PrintOutput(size_t index, size_t elementsPerRow = 16);
    bool RunOp();

private:
    size_t numInputs_;
    size_t numOutputs_;

    std::vector<aclDataBuffer *> inputBuffers_;
    std::vector<aclDataBuffer *> outputBuffers_;

    std::vector<void *> devInputs_;
    std::vector<void *> devOutputs_;

    std::vector<void *> hostInputs_;
    std::vector<void *> hostOutputs_;
    OperatorDesc *opDesc_;
};

#endif // COMMON_H
